/**
 * 
 */
package com.radeonsys.jsfmock.jsf.context;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseStream;
import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKit;

/**
 * TODO
 * 
 * @see javax.faces.context.FacesContext
 * 
 * @author Ranganath Kini (ranganathkini at gmail dot com)
 * @version 1.0
 */
public class MockFacesContext extends FacesContext {
	
	private static final String DEFAULT_CLIENT_ID = "jsf.default.client.id";
	
	private final ExternalContext _externalContext;
	private final Application _application;
	
	private final Map<String, List<FacesMessage>> messages = 
		new HashMap<String, List<FacesMessage>>();
	
	private boolean _responseComplete;
	private boolean _renderResponse;

	/**
	 * Creates a new instance of <code>MockFacesContext</code>
	 */
	private MockFacesContext(final ExternalContext externalContext, 
			final Application application) {
		_externalContext = externalContext;
		_application = application;
	}
	
	/**
	 * Builds a new {@link MockFacesContext} and registers it with the
	 * {@link FacesContext} so that it is returned when the client calls
	 * {@link FacesContext#getCurrentInstance()} method.
	 * 
	 * @param externalContext
	 * @param application
	 * @return
	 */
	public static final FacesContext buildMockFacesContext(final ExternalContext externalContext, 
			final Application application) {
		FacesContext result = new MockFacesContext(externalContext, application);
		FacesContext.setCurrentInstance(result);
		return result;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#addMessage(java.lang.String, javax.faces.application.FacesMessage)
	 */
	@Override
	public void addMessage(String clientId, FacesMessage message) {
		//Assert.notNull(message, "Message must be specified");
		
		if (clientId == null)
			clientId = DEFAULT_CLIENT_ID;
		
		if (messages.containsKey(clientId))
			messages.get(clientId).add(message);
		else	
			messages.put(clientId, new ArrayList<FacesMessage>(
					Collections.singletonList(message)));	
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getApplication()
	 */
	@Override
	public Application getApplication() {
		return _application;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getClientIdsWithMessages()
	 */
	@Override
	public Iterator<String> getClientIdsWithMessages() {
		return messages.keySet().iterator();
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getExternalContext()
	 */
	@Override
	public ExternalContext getExternalContext() {
		return _externalContext;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getMaximumSeverity()
	 */
	@Override
	public Severity getMaximumSeverity() {
		return FacesMessage.SEVERITY_FATAL;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getMessages()
	 */
	@Override
	public Iterator<FacesMessage> getMessages() {
		List<FacesMessage> result = new ArrayList<FacesMessage>();
		
		for (String clientId : messages.keySet())
			result.addAll(messages.get(clientId));
		
		return Collections.unmodifiableList(result).iterator();
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getMessages(java.lang.String)
	 */
	@Override
	public Iterator<FacesMessage> getMessages(String clientId) {
		if (clientId == null)
			clientId = DEFAULT_CLIENT_ID;
		
		@SuppressWarnings("unchecked")
		List<FacesMessage> msgs = messages.containsKey(clientId) ? 
				messages.get(DEFAULT_CLIENT_ID) : 
					(List<FacesMessage>)Collections.EMPTY_LIST;
		
		return Collections.unmodifiableList(msgs).iterator();
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getRenderKit()
	 */
	@Override
	public RenderKit getRenderKit() {
		return null;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getRenderResponse()
	 */
	@Override
	public boolean getRenderResponse() {
		return _renderResponse;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getResponseComplete()
	 */
	@Override
	public boolean getResponseComplete() {
		return _responseComplete;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getResponseStream()
	 */
	@Override
	public ResponseStream getResponseStream() {
		return null;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getResponseWriter()
	 */
	@Override
	public ResponseWriter getResponseWriter() {
		return null;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#getViewRoot()
	 */
	@Override
	public UIViewRoot getViewRoot() {
		return null;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#release()
	 */
	@Override
	public void release() {
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#renderResponse()
	 */
	@Override
	public void renderResponse() {
		_renderResponse = true;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#responseComplete()
	 */
	@Override
	public void responseComplete() {
		_responseComplete = true;
	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#setResponseStream(javax.faces.context.ResponseStream)
	 */
	@Override
	public void setResponseStream(ResponseStream responseStream) {

	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#setResponseWriter(javax.faces.context.ResponseWriter)
	 */
	@Override
	public void setResponseWriter(ResponseWriter responseWriter) {

	}

	/* (non-Javadoc)
	 * @see javax.faces.context.FacesContext#setViewRoot(javax.faces.component.UIViewRoot)
	 */
	@Override
	public void setViewRoot(UIViewRoot root) {
	}
}
